local mysql=require("resty.mysql")
local config=require("config.application")
local application=require("vanilla.v.application")
local sys_statck=require('vanilla.v.ctxstatck'):sys_statck()

local s_foramt=string.format
--local s_gub = ngx.re.gsub
local s_gub = string.gsub

local db_M={}
local self_init=false;

local mysqlEscapeMode = "[%z\'\"\\\26\b\n\r\t]";  
local mysqlEscapeReplace = {  
    ['\0']='\\0',  
    ['\''] = '\\\'',  
    ['\"'] = '\\\"',  
    ['\\'] = '\\\\',  
    ['\26'] = '\\z',  
    ['\b'] = '\\b',  
    ['\n'] = '\\n',  
    ['\r'] = '\\r',  
    ['\t'] = '\\t',  
}; 

function db_M:new(tablestr,as)
	if not ngx.ctx['table_cache'] then
		ngx.ctx['table_cache']={}
	end
	
	if type(ngx.ctx['table_cache'][tablestr])=='table' then
		--print_r("cached")
		return ngx.ctx['table_cache'][tablestr]
	end

	as=as or ""

	local obj = {
		table=config.mysql.pre..tablestr,
		tableas=as,
		wherestr={},
		joins={},
		orderstr="",
		selectstr="",
		limitstr="",
		keepstr=false
	}
	
	setmetatable(obj,{__index=self})
	ngx.ctx['table_cache'][tablestr]=obj
	return obj
end

function db_M:query( sql,num )
	local db,err = mysql:new()
	if not db then
		self:raise_syserror("create mysql error");
	end

	db:set_timeout(3000)

	local ok,err,errno,sqlstate = db:connect(config.mysql)
	if not ok then
		self:raise_syserror("mysql connect error");
	end

	local res, err, errno, sqlstate =db:query(sql, num)
	if not res then
		self:raise_syserror("mysql query error:"..err.."<br/>"..sql);
	end

	local ok, err = db:set_keepalive(10000, 100)
	if not ok then
		self:raise_syserror("mysql set_keepalive error");
	end

	return res
end

function db_M:raise_syserror(error)
	sys_statck:get():errResponse({code=1000, msg={["msg"]=error}});
end


function db_M:update(data)
	if type(data)~='table' then return false end
	local t = self:make_data(data)
	local sql="update "..self.table.." set " ..table.concat(t,",") ..self:make_where()
	return self:query(sql);
end

function db_M:insert(data)
	if type(data)~='table' then return false,nil end
	local t = self:make_data(data)
	local sql="insert into "..self.table.." set " ..table.concat(t,",");
	return self:query(sql);
end

function db_M:replace(data)
	if type(data)~='table' then return false,nil end
	local t = self:make_data(data)
	local sql="replace into "..self.table.." set " ..table.concat(t,",");
	return self:query(sql);
end

function db_M:make_data(data)
	local t={};
	for k,v in pairs(data) do
		if type(v)~='userdata' then
			t[#t+1]="`"..k.."`="..self:escape(v)
		else
			t[#t+1]="`"..k.."`=null "	
		end
	end
	return t;
end

function db_M:delete()
	local sql="delete from "..self:make_table()..self:make_where()..self:make_order()..self:make_limit()
	self:clear()
	return self:query(sql);
end

function db_M:count(field)
	field=field or "*"
	local sql="select count("..field..") t from "..self:make_table()..self:make_where().." limit 1"
	self:clear()
	local res = self:query(sql,1);
	if res[1] then 
		return res[1]['t'];
	end
	return 0
end


function db_M:get_list(numnum )
	local sql="select "..self:make_select().." from "..self:make_table()..self:make_where()..self:make_order()..self:make_limit()
	self:clear()
	return self:query(sql,numnum)
end

function db_M:get_one()
	local sql="select "..self:make_select().." from "..self:make_table() ..self:make_where()..self:make_order()..self:make_limit()
	self:clear()
	local res=self:query(sql,1)
	return res[1]
end


function db_M:replace_data(t,val)
	if val==nil then
		val=''
	end
	local typet=type(t)
	if typet=='table' then
		for k,v in pairs(t) do
			t[k]=self:replace_data(v,val)
		end
	else
		if type(t)=="userdata" then
			t=val
		end
	end
	return t
end

function db_M:keep(is_keep)
	self.keepstr=is_keep;
end

function db_M:select( fileds )
	self.selectstr=fileds
	return self;
end

function db_M:order(orderstr)
	self.orderstr=orderstr
	return self
end

function db_M:leftjoin(tablestr,on)
	self.joins[#self.joins+1]="left join "..config.mysql.pre..tablestr.." on "..on
	return self
end

function db_M:join(tablestr,on)
	self.joins[#self.joins+1]="join "..config.mysql.pre..tablestr.." on "..on
	return self
end

function db_M:where(sql,...)
	local sql=s_gub(sql,'?',"%%s");
	local params={...}

	local endparm=params[#params]

	if type(endparm)==false then
		params[#params]=nil
	end

	--print_r(params)
	for k,v in ipairs(params) do
		if endparm==false then
			params[k]=ngx.re.sub(self:escape(v),"^'(.+)'$",'$1') 
		else
			params[k]=self:escape(v)
		end
	end

	self.wherestr[#self.wherestr+1]=s_foramt(sql,unpack(params))
	return self
end 

function db_M:limit(limitstr)
	self.limitstr=limitstr
	return self
end

function db_M:clear(force)
	if self.keepstr and not force then return nil end
	self.orderstr=''
	self.wherestr={}
	self.joins={}
	self.limitstr=''
	self.selectstr=''
end

function db_M:make_table()
	return self.table.." "..self.tableas.." "..table.concat(self.joins," ")
end

function db_M:make_where()
	local where = ""
	if self.wherestr and  _G.next(self.wherestr) then
		where= table.concat(self.wherestr," and ")
	end

	if where ~="" then
		return " where "..where
	end
	return where
end

function db_M:make_order()
	local order= self.orderstr or ""
	if order ~="" then
		return " order by "..order
	end
	return order
end

function db_M:make_limit()
	local limit= self.limitstr or ""
	if limit ~="" then
		return " limit "..limit
	end
	return limit
end

function db_M:make_select()
	local select= self.selectstr or "*"
	if select ~="" then
		return " ".. select.." "
	end
	return "*"
end

function db_M:escape(s)
	s=tostring(s)
	return ngx.quote_sql_str(s)
	--return s_gub(s, mysqlEscapeMode, mysqlEscapeReplace)
end

return db_M